/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package sdc.net.server.comp;

import java.util.List;
import java.util.Map;
//import java.util.Map.Entry;
import sdc.component.type.ComponentType;
//import sdc.component.type.ComponentTypePool;
import sdc.connection.ConnectionManager;
import sdc.connection.OverlayConnectionManager;
import sdc.msg.Message;
import sdc.msg.construct.*;
import sdc.msg.use.*;
import sdc.net.MessageBuffer;
import sdc.net.server.MessageSender;
import sdc.overlay.InitialOverlayConstructor;
import sdc.Address;
import sdc.msg.sla.ChangeReplyMessage;
import sdc.msg.sla.ReplicateReplyMessage;

/**
 *
 * @author yokoe
 */
public class MessageHandler {

	/**
	 * ???b?Z?[?W??????A?????????????B
	 * @param message
	 */
	public static void receiveMessage(ComponentServer componentServer, Message message)
	{
		if (message == null)
			throw new NullPointerException("Received message is null!");

		MessageSender messageSender = componentServer.getMessageSender();
		OverlayConnectionManager overlayConnectionManager = componentServer.getOverlayConnectionManager();
		ConnectionManager useConnectionManager = componentServer.getUseConnectionManager();
		MessageBuffer messageBuffer = componentServer.getMessageBuffer();
		//WorkQueue workQueue = componentServer.getWorkQueue();


		ComponentType type = message.getType();


		switch (message.getID())
		{
			case ADDRESS_REQUEST:
				if (componentServer.isFailed())
					messageSender.sendMessage(new AddressReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, true));
				else
					messageSender.sendMessage(new AddressReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, overlayConnectionManager.getNeighborsAndMyAddressList(type)));
				break;

			case ADDRESS_REPLY:
				if (message.isNull() == false)
					((AddressReplyMessage) message).getAddressList().remove(componentServer.getAddress());
					((AddressReplyMessage) message).getAddressList().removeAll(overlayConnectionManager.getNeighborsAndMyAddressList(type));
					messageSender.sendOpenConnectionRequest(type, componentServer, ((AddressReplyMessage) message).getAddressList());
				break;

			case OPEN_CONNECTION_REQUEST:

				if (overlayConnectionManager.checkOpeningConnection(type, message.getSrcAddress()) == true)
				{
					messageSender.sendMessage(new OpenConnectionReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, message.getTripTime(), componentServer.getEwma(type)));
				} else
				{
					messageSender.sendMessage(new OpenConnectionReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, true));
				}
				break;

			case OPEN_CONNECTION_REPLY:
				if (messageBuffer.pushMessage(message) == true)
				{
					InitialOverlayConstructor.connectOverlayLinks(componentServer);
				}
				break;

			case OPEN_CONNECTION_ACKNOWLEDGE:
				boolean decrementOK = true;

				if (overlayConnectionManager.getConnectionSum() >= overlayConnectionManager.getMaxConnection())
					decrementOK = InitialOverlayConstructor.decrementConnection(componentServer);

				if (decrementOK == true)
					overlayConnectionManager.addConnection(type, message.getSrcAddress());
				break;

			case CLOSE_CONNECTION_REQUEST:

				if (overlayConnectionManager.hasRemovableLink(type) == true)
				{
					overlayConnectionManager.removeConnection(type, message.getSrcAddress());

					messageSender.sendMessage(new CloseConnectionReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type));
				}
				break;

			case CLOSE_CONNECTION_REPLY:
				overlayConnectionManager.removeConnection(type, message.getSrcAddress());
				break;

			case DEGREE_REQUEST:
				messageSender.sendMessage(new DegreeReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, message.getTripTime(), overlayConnectionManager.getConnectionNum(type)));
				break;

			case DEGREE_REPLY:
				if (messageBuffer.pushMessage(message) == true)
				{
					messageSender.sendCloseConnectionRequest(componentServer);
				}
				break;

			case SERVICE_TIME_REQUEST:
				double externalPerformance = componentServer.getEwma(type);
				messageSender.sendMessage(new ServiceTimeReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, message.getTripTime(), externalPerformance));
				break;

                        case SERVICE_TIME_REPLY:
                            if(messageBuffer.pushMessage(message) == true){
                                componentServer.sendUseRequest();
                            }
                                break;

			case USE_REQUEST:
				useConnectionManager.addConnection(type, message.getSrcAddress());
				messageSender.sendMessage(new UseReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, overlayConnectionManager.getNeighborsAndMyAddressList(type),(double)message.getTripTime()));
                                if(componentServer.requestNumMap.containsKey(type)){
                                    componentServer.requestNumMap.put(type, componentServer.requestNumMap.get(type)+1);
                                }
                                else{
                                    componentServer.requestNumMap.put(type, 1);
                                }
                                break;

                        case USE_REPLY:
                            List<Address> addressList = ((UseReplyMessage)message).getAddressList();
                            componentServer.useComponent(type, message.getSrcAddress(), addressList);
                            componentServer.tripTimeMap.put(type, message.getTripTime());
                            break;
                            
			case INTENSITY_REQUEST:
				messageSender.sendMessage(new IntensityReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, message.getTripTime(), componentServer.getEwma(type)));
				break;

			case INTENSITY_REPLY:
				if (messageBuffer.pushMessage(message) == true)
				{
					componentServer.decideWhichNodeToAppend();
				}
				break;



			case FAILURE_NOTICE:
				overlayConnectionManager.removeConnection(type, message.getSrcAddress());
				break;

                        case TAT_REQUEST:
                                //double TAT = componentServer.getEwma(message.getType()) + message.getTripTime() * componentServer.w;
                                //componentServer.setPartialTAT(message.getType(), TAT);
                                //componentServer.sendTATRequest(message.getType());
                                break;

                        case TAT_REPLY:
                                //
                                break;

                        case SERVICE_TIME_REQUEST_2:
                            messageSender.sendMessage(new ServiceTimeReplyMessage2(componentServer.getAddress(),message.getSrcAddress(),type));
                            break;
                        case SERVICE_TIME_REPLY_2:
                            componentServer.useComponentMap.put(type, message.getSrcAddress());
                            componentServer.tripTimeMap.put(type, message.getTripTime());
                            break;

                        case CHANGE_REQUEST:
                            messageSender.sendMessage(new ChangeReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, componentServer.getCapableRequest(type)));
                            break;
                        case CHANGE_REPLY:
                            if(messageBuffer.pushMessage(message) == true) {
                                componentServer.change(type);
                            }
                            break;
                        case REPLICATE_REQUEST:
                            messageSender.sendMessage(new ReplicateReplyMessage(componentServer.getAddress(), message.getSrcAddress(), type, componentServer.serviceTimeAfterReplication(type, message.getOption())));
                            break;
                        case REPLICATE_REPLY:
                            if(messageBuffer.pushMessage(message) == true) {
                                componentServer.replicate(type);
                            }
                            break;

                        default:
                            throw new RuntimeException("There does not exist [MessageHandler].");


		}
	}
}